package com.example.websocket.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

// 使用消息代理，参考 https://springdoc.cn/spring/web.html#websocket-stomp
@Configuration
@EnableWebSocketMessageBroker   // 启用 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private TaskScheduler messageBrokerTaskScheduler;

    @Autowired
    public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
        this.messageBrokerTaskScheduler = taskScheduler;
    }

    // 可以通过在线网站 https://wstool.js.org/ 使用 ws://localhost:9003/stomp 进行连接测试
    // 定义客户端如何连接到服务器的 WebSocket 端点
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // /portfolio 是WebSocket（或SockJS）客户端需要连接以进行WebSocket握手的端点的HTTP URL。相当于是 WebSocket 的服务端连接地址。格式为 ws://localhost:9003/stomp
        // 注册 STOMP 端点，允许 SockJS 客户端连接
        registry.addEndpoint("/stomp")
                // 允许所有来源，生产环境中应更加严格，参考 https://springdoc.cn/spring/web.html#websocket-server-allowed-origins
                .setAllowedOriginPatterns("*")
                // .setAllowedOrigins("*")
                // 添加拦截器，用于拦截握手请求。将 HttpSession 对象传递到 WebSocketSession 中
                // 它允许你在 WebSocket 握手请求（即客户端与服务器建立连接的初始 HTTP 请求）到达之前或之后执行自定义逻辑。
                // 这个拦截器的主要作用是将现有的 HTTP 会话信息（如 HttpSession）关联到 WebSocket 会话上。
                // .addInterceptors(new HttpSessionHandshakeInterceptor())
                // 指定这些端点是否支持 SockJS（一种提供WebSocket功能的浏览器库，可以在不支持WebSocket的环境中使用）
                // 是否启用 SockJS 协议，这对于那些可能不直接支持 WebSocket 的旧版浏览器非常重要。
                .withSockJS();
    }

    // 配置消息代理的行为，它决定了消息是如何在服务器内部进行路由的。你可以配置简单的内存消息代理，
    // 也可以集成外部的消息代理服务（如 RabbitMQ、ActiveMQ 等），并且定义应用程序的目的地前缀以及广播目的地等。
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 启用简单的内存消息代理，用于广播消息到所有客户端
        // 配置参考 https://springdoc.cn/spring/web.html#websocket-stomp-handle-simple-broker
        registry.enableSimpleBroker("/queue", "/topic")
                // 设置心跳间隔时间，单位为毫秒，默认为 30000 毫秒（30 秒）
                // 这用于保持连接的活跃性，以保持连接的打开状态。
                .setHeartbeatValue(new long[]{30000, 0})
                // 设置消息代理的任务调度器，用于处理消息代理相关的任务，例如心跳检查等
                .setTaskScheduler(messageBrokerTaskScheduler);

        // enableStompBrokerRelay 启用外部的消息代理服务（例如： RabbitMQ、RocketMQ 等），用于广播消息到所有客户端。默认情况下，使用内存消息代理
        // 配置参考官网 https://springdoc.cn/spring/web.html#websocket-stomp-handle-broker-relay
        // registry.enableStompBrokerRelay("/queue", "/topic");

        // 设置应用程序目的地前缀，例如所有的"/app"路径都由控制器处理
        registry.setApplicationDestinationPrefixes("/app");
        // 确保在同一会话中发布的消息按顺序传递
        registry.setPreservePublishOrder(true);
    }

    // 在消息到达目的地之前对其进行拦截和处理，可以用于 日志记录、身份验证、限流等。
    // 参考官方文档 https://springdoc.cn/spring/web.html#websocket-stomp-authentication-token-based
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        // 添加一个拦截器，它会在每个入站消息上触发
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                // 打印消息的内容（仅作示例用途）
                System.out.println("Received message: " + message);
                return message; // 继续发送消息到下一个处理器
            }
        });
    }
}
